# -*- coding: utf-8 -*-
# @Time    : 2025/6/13 0:29
# @Author  : wsxzy98@outlook.com
# @File    : stock_package.py
# @Software: VScode

from odoo import fields, models
from odoo.exceptions import UserError
from odoo import api
from collections import defaultdict

#包裹状态管理
class StockQuantPackage(models.Model):
    _inherit = 'stock.quant.package'

    x_synced_customer_reference = fields.Boolean('客户参考号已同步', default=False)

    x_lock_state = fields.Selection([
        ('unlocked', '未封箱'),
        ('locked',   '已封箱'),
        ('shipped',  '已发货'),
    ], string='包裹状态', default='unlocked')

    # x_shipped_date = fields.Datetime('发货时间', readonly=True)
    # x_shipped_user_id = fields.Many2one('res.users', '发货操作人', readonly=True)
    # x_ship_note = fields.Char('发货备注')

    x_line_ids = fields.One2many('x.package.line', 'package_id', string='内容行')#没用上，以后可能有用

    preview_move_line_ids = fields.One2many(
    'stock.move.line', 
    'result_package_id', 
    string='预览拣货明细'
    )

    preview_merged_quant_line_ids = fields.One2many(
    'x.package.quant.line', 
    'package_id',
    string='汇总预览明细'
    )

    preview_total_qty = fields.Float(string="包裹内总数量", compute= '_compute_preview_totals', store=False)
    preview_total_price = fields.Float(string="包裹内总销售额", compute='_compute_preview_totals', store=False)

    def action_ship(self):
        """
        发货：将包裹内所有 quant 解绑为散货，并把状态置为已发货
        采用“克隆/合并到散货 + 删除原行”的方式，确保 customer_reference 原样保留，
        且不同参考号不会混在一起。
        """
        Quant = self.env['stock.quant']

        for pkg in self:
            if not pkg.quant_ids:
                continue

            # 可选：状态检查
            if getattr(pkg, 'x_lock_state', 'unlocked') != 'locked':
                raise UserError(('请先封箱后再发货。'))

            # 一条条处理，避免写同一条触发覆盖
            for q in pkg.quant_ids.sorted('id'):
                if q.reserved_quantity:
                    raise UserError(_('包裹内有已预留的库存（%s），请先释放预留。') % q.product_id.display_name)

                target_domain = [
                    ('product_id', '=', q.product_id.id),
                    ('location_id', '=', q.location_id.id),
                    ('lot_id', '=', q.lot_id.id if q.lot_id else False),
                    ('owner_id', '=', q.owner_id.id if q.owner_id else False),
                    ('package_id', '=', False),
                    ('company_id', '=', q.company_id.id if q.company_id else False),
                    # 关键：同一个 customer_reference 才能合并到一行
                    ('customer_reference', '=', q.customer_reference or False),
                ]
                target = Quant.search(target_domain, limit=1)

                if target:
                    # 合并到已有散货行（同参考号）
                    target.sudo().write({
                        'quantity': target.quantity + q.quantity,
                    })
                    # 删除原包裹行
                    q.sudo().unlink()
                else:
                    # 克隆一条散货 quant（原样保留客户参考号等字段）
                    vals = {
                        'product_id': q.product_id.id,
                        'location_id': q.location_id.id,
                        'company_id': q.company_id.id if q.company_id else False,
                        'lot_id': q.lot_id.id if q.lot_id else False,
                        'owner_id': q.owner_id.id if q.owner_id else False,
                        'quantity': q.quantity,
                        'reserved_quantity': 0.0,
                        'package_id': False,                       # 变散货
                        'origin_name': q.origin_name or False,
                        'customer_reference': q.customer_reference or False,
                        'pack_datetime': q.pack_datetime or False,
                        # 其他你在 quant 上加的自定义字段都可以照抄过来
                    }
                    Quant.sudo().create(vals)
                    # 删除原包裹行
                    q.sudo().unlink()

            # 可选：把包裹状态标为“已发货”
            if hasattr(pkg, 'x_lock_state'):
                pkg.sudo().write({'x_lock_state': 'shipped'})

        return True

    @api.depends('preview_merged_quant_line_ids.quantity', 'preview_merged_quant_line_ids.total_price')
    def _compute_preview_totals(self):
        for package in self:
            lines = package.preview_merged_quant_line_ids
            package.preview_total_qty = sum(lines.mapped('quantity'))
            package.preview_total_price = sum(lines.mapped('total_price'))
    
    def _compute_preview_merged_quant_lines(self):
        QuantLine = self.env['x.package.quant.line']
        Product = self.env['product.product']
        for package in self:
            # 删除旧的记录
            QuantLine.search([('package_id', '=', package.id)]).unlink()

            # 合并 preview_move_line_ids 的数据
            grouped = {}
            for line in package.preview_move_line_ids:
                key = (line.product_id.id, line.customer_reference or '')
                if key not in grouped:
                    grouped[key] = {
                        'quantity': 0.0,
                        'total_price': 0.0,
                    }
                grouped[key]['quantity'] += line.qty_done
                grouped[key]['total_price'] += line.sale_price or 0.0  # sale_price 实际是该明细总价

            # 写入合并明细
            for (product_id, customer_ref), data in grouped.items():
                product = Product.browse(product_id)
                origin = product.product_tmpl_id.origin or ''
                qty = data['quantity']
                total_price = data['total_price']
                unit_price = total_price / qty if qty else 0.0

                QuantLine.create({
                    'package_id': package.id,
                    'product_id': product_id,
                    'customer_reference': customer_ref,
                    'quantity': qty,
                    'sale_price': total_price,   # 总价
                    'unit_price': unit_price,    # 单价（计算字段）
                    'origin_name': origin,       # 原产地
            })
        
    def action_unlock_package(self):
        for rec in self:
            rec.x_lock_state = 'unlocked'

    def action_lock_package(self):
        for rec in self:
            rec.x_lock_state = 'locked'
            rec.action_sync_customer_reference() 
            rec.action_sync_origin_name()
            rec.pack_date = fields.Date.today()
            rec._compute_preview_merged_quant_lines()

    # def action_clear_package(self):
    #     for rec in self:
    #         if rec.x_lock_state != 'unlocked':
    #             raise UserError('包裹已锁定，请先拆包再清空内容。')
    #         rec.quant_ids = [(5, 0, 0)]

    def action_open_edit_list(self):
        self.ensure_one()
        if self.x_lock_state != 'unlocked':
            raise UserError('包裹已锁定，无法编辑内容，请先拆包！')
        return {
            'type': 'ir.actions.act_window',
            'name': '编辑包裹内容',
            'res_model': 'package.edit.wizard',
            'view_mode': 'form',
            'target': 'new',
            'context': {'active_id': self.id}
        }
    
    def action_sync_customer_reference(self):
        for pkg in self:
            quants = pkg.quant_ids.sorted('id')

            # 根据本包裹中的 move line 建“池子”，按(产品, 数量)分桶，记录每个桶内的参考号列表
            mls = self.env['stock.move.line'].search([
                ('result_package_id', '=', pkg.id),
                ('qty_done', '>', 0),
            ], order='id')

            pool = defaultdict(list)  # key: (product_id, qty) -> [ref, ref, ...]
            for ml in mls:
                key = (ml.product_id.id, float(ml.qty_done))
                pool[key].append(ml.customer_reference or '')

            used_idx = defaultdict(int)  # 每个桶已分配到第几个参考号

            for q in quants:
                key = (q.product_id.id, float(q.quantity))

                # 关键：已有值的，一律保留，不覆盖
                if q.customer_reference:
                    # 顺便推进一下游标，避免后面的空行再拿到同一个参考号
                    refs = pool.get(key, [])
                    if refs:
                        try:
                            pos = refs.index(q.customer_reference, used_idx[key])
                            used_idx[key] = pos + 1
                        except ValueError:
                            # 当前参考号不在池子里就忽略
                            pass
                    continue

                # 为空的，才从池子里按顺序补一个参考号
                refs = pool.get(key, [])
                if used_idx[key] < len(refs):
                    q.customer_reference = refs[used_idx[key]]
                    used_idx[key] += 1
                # 如果池子里也没有，就留空（尊重手工）
        
    @api.model
    def action_sync_origin_name(self):
        """
        同步本包裹下所有 quant 的原产地字段，来源于产品主数据。
        可在封箱、修正数据时手动或自动调用。
        """
        for package in self:
            quants = self.env['stock.quant'].search([('package_id', '=', package.id)])
            for quant in quants:
                if quant.product_id and quant.product_id.product_tmpl_id.origin:
                    quant.origin_name = quant.product_id.product_tmpl_id.origin

    def action_sync_new_serial_number(self):
        """
        同步包裹下所有 quant 的新编码字段，来源于产品主数据。
        """
        for package in self:
            quants = self.env['stock.quant'].search([('package_id', '=', package.id)])
            for quant in quants:
                if quant.product_id and quant.product_id.product_tmpl_id.new_variable:
                    quant.new_serial_number = quant.product_id.product_tmpl_id.new_variable
        
    # @api.depends('id')
    # def _compute_preview_move_line_ids(self):
    #     """自动计算：本包裹所有 result_package_id=本包裹id 的拣货明细行"""
    #     for package in self:
    #         package.preview_move_line_ids = self.env['stock.move.line'].search([
    #             ('result_package_id', '=', package.id)
    #         ])

class XPackageLine(models.TransientModel):
    _name = 'x.package.line'
    _description = '临时包裹内容'
    package_id = fields.Many2one('stock.quant.package', string='包裹')
    product_id = fields.Many2one('product.product', string='产品')
    lot_id = fields.Many2one('stock.lot', string='批次/序列号')
    quantity = fields.Float('数量')
    origin_name = fields.Char('原产地')

class XPackageQuantLine(models.Model):
    _name = 'x.package.quant.line'
    _description = '包裹合并明细展示'

    package_id = fields.Many2one('stock.quant.package', string='包裹', ondelete='cascade')
    product_id = fields.Many2one('product.product', string='产品')
    customer_reference = fields.Char('客户参考号')
    quantity = fields.Float('数量')
    sale_price = fields.Float('销售价')
    unit_price = fields.Float('销售单价', compute='_compute_unit_price', store=False)
    total_price = fields.Float('总价', compute='_compute_total_price', store=False)
    origin_name = fields.Char('原产地')

    @api.depends('quantity', 'sale_price')
    def _compute_total_price(self):
        for line in self:
            line.total_price = line.sale_price
    
    @api.depends('sale_price', 'quantity')
    def _compute_unit_price(self):
        for rec in self:
            rec.unit_price = rec.sale_price / rec.quantity if rec.quantity else 0.0

